import sys
import ctypes
import pyautogui
import time
import keyboard
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QHBoxLayout, QTextEdit 
from PyQt5.QtWidgets import QLabel, QShortcut, QInputDialog, QFrame, QMessageBox
from PyQt5.QtCore import QThread, pyqtSignal, QPoint, Qt
from PyQt5.QtGui import QIcon, QKeySequence
import os

# 确保共享库路径正确
clib_path = os.path.join(os.path.dirname(__file__), 'mousecontrolHotkey.dll')
clib = ctypes.CDLL(clib_path)

# 定义键值对结构体
class KeyValuePair(ctypes.Structure):
    _fields_ = [
        ("hotkey", ctypes.c_char * 20),
        ("keyvalue", ctypes.c_char * 20)
    ]

# 定义快捷键结构体
class PointKeys(ctypes.Structure):
    _fields_ = [
        ("khorizontal", KeyValuePair),
        ("kvertical", KeyValuePair),
        ("ktoggle", KeyValuePair),
        ("kcustom", KeyValuePair)
    ]

# 定义C语言函数的参数和返回类型
clib.init_shortcut_keys_c.argtypes = []
clib.init_shortcut_keys_c.restype = None
clib.load_shortcut_keys_c.argtypes = []
clib.load_shortcut_keys_c.restype = ctypes.POINTER(PointKeys)
clib.modify_shortcut_key_c.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
clib.modify_shortcut_key_c.restype = None
clib.control_mouse_position_c.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_double, ctypes.c_double]
clib.control_mouse_position_c.restype = None

class MouseControlApp(QWidget):
    shortcutdict = {'horizontal': 'ctrl+h', 'vertical': 'ctrl+t', 'custom': 'ctrl+p', 'toggle': 'ctrl+o'}

    def __init__(self):
        super().__init__()
        self.initUI()
        self.setWindowTitle('MouseFreeAxis')
        self.setWindowIcon(QIcon('icon.png'))
        self.close_shortcut = QShortcut('ctrl+w', self)
        self.close_shortcut.activated.connect(self.close)

        self.direction = 0
        self.is_active = False
        self.last_mouse_pos = QPoint(*pyautogui.position())
        self.amplitude_limit = 0.1

        self.mouse_control_thread = MouseControlThread(self)
        self.mouse_control_thread.start()

    def initUI(self):
        layout = QVBoxLayout()

        # 初始化按钮和快捷键的UI布局
        self.create_shortcut_ui(layout, 'horizontal', "水平移动\nHorizontal move", 0)
        self.create_shortcut_ui(layout, 'vertical', "垂直移动\nVertical move", 90)
        self.create_custom_ui(layout)
        self.create_toggle_ui(layout)

        self.setLayout(layout)

        clib.init_shortcut_keys_c()
        self.loadShortcut()

    def modifyShortcut(self, key):
        text, ok = QInputDialog.getText(self, "修改快捷键\nModify hotkey", "请输入新的快捷键（如：ctrl+u）\nInput new hotkey(e.g. ctrl+u)):")
        if ok:
            try:
                clib.modify_shortcut_key_c(key.encode('utf-8'), text.encode('utf-8'))
                self.loadShortcut()
            except Exception as e:
                QMessageBox.critical(self, "错误(Error)", "修改快捷键时发生错误(Error while modifying shortcut key): {e}")

    def loadShortcut(self):
        try:
            point_keys = clib.load_shortcut_keys_c()
            if point_keys:
                pk = point_keys.contents
                self.shortcutdict = {
                    "horizontal": ctypes.string_at(pk.khorizontal.keyvalue).decode('utf-8').rstrip('\x00'),
                    "vertical": ctypes.string_at(pk.kvertical.keyvalue).decode('utf-8').rstrip('\x00'),
                    "custom": ctypes.string_at(pk.ktoggle.keyvalue).decode('utf-8').rstrip('\x00'),
                    "toggle": ctypes.string_at(pk.kcustom.keyvalue).decode('utf-8').rstrip('\x00'),
                }
                self.updateShortcut()
                self.updateShortcutLabels()
        except Exception as e:
            QMessageBox.critical(self, "错误(Error)", "加载快捷键时发生错误(Error while loading shortcut keys): {e}")

    def updateShortcut(self):
        keyboard.unhook_all()
        for key, shortcut in self.shortcutdict.items():
            if key == "horizontal":
                keyboard.add_hotkey(shortcut, lambda: self.set_direction(0, True, self.horizontal_btn))
            elif key == "vertical":
                keyboard.add_hotkey(shortcut, lambda: self.set_direction(90, True, self.vertical_btn))
            elif key == "custom":
                keyboard.add_hotkey(shortcut, self.set_custom_direction)
            elif key == "toggle":
                keyboard.add_hotkey(shortcut, self.toggle_control)

    def updateShortcutLabels(self):
        for key, label in self.__dict__.items():
            if key.endswith('_shortcut_label'):
                label.setText(f'快捷键(Hotkey):\n {self.shortcutdef(key.replace("_shortcut_label", ""))}')

    def create_shortcut_ui(self, layout, key, text, direction):
        btn = QPushButton(text)
        btn.clicked.connect(lambda: self.set_direction(direction, True, btn))
        layout.addWidget(btn)

        shortcut_label = QLabel(f'快捷键(Hotkey):\n {self.shortcutdef(key)}')
        modify_btn = QPushButton("修改快捷键\nModify hotkey")
        modify_btn.clicked.connect(lambda: self.modifyShortcut(key))
        horizontal_layout = QHBoxLayout()
        horizontal_layout.addWidget(shortcut_label)
        horizontal_layout.addWidget(modify_btn)
        layout.addLayout(horizontal_layout)

        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
        layout.addWidget(line)

        # 保存按钮和标签
        setattr(self, f'{key}_btn', btn)
        setattr(self, f'{key}_shortcut_label', shortcut_label)

    def create_custom_ui(self, layout):
        custom_layout = QHBoxLayout()
        custom_layout.addWidget(QLabel('自定义方向:\n(Customize direction:)'))
        self.direction_input = QTextEdit()
        self.direction_input.setPlaceholderText('输入角度 (°)\nInput angle (°)')
        self.direction_input.setFixedHeight(40)
        custom_layout.addWidget(self.direction_input)
        self.custom_btn = QPushButton('设置方向\nSet direction')
        self.custom_btn.clicked.connect(self.set_custom_direction)
        custom_layout.addWidget(self.custom_btn)
        layout.addLayout(custom_layout)

        self.custom_shortcut_label = QLabel(f'快捷键(Hotkey): {self.shortcutdef("custom")}')
        modify_custom_shortcut_btn = QPushButton("修改快捷键\nModify hotkey")
        modify_custom_shortcut_btn.clicked.connect(lambda: self.modifyShortcut('custom'))
        custom_shortcut_layout = QHBoxLayout()
        custom_shortcut_layout.addWidget(self.custom_shortcut_label)
        custom_shortcut_layout.addWidget(modify_custom_shortcut_btn)
        layout.addLayout(custom_shortcut_layout)

        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
        layout.addWidget(line)

    def create_toggle_ui(self, layout):
        self.activate_btn = QPushButton("激活/停用方向控制\nActivate/Deactivate direction control")
        self.activate_btn.clicked.connect(self.toggle_control)
        layout.addWidget(self.activate_btn)

        self.toggle_shortcut_label = QLabel(f'快捷键(Hotkey):\n {self.shortcutdef("toggle")}')
        modify_toggle_shortcut_btn = QPushButton("修改快捷键\nModify hotkey")
        modify_toggle_shortcut_btn.clicked.connect(lambda: self.modifyShortcut('toggle'))
        toggle_shortcut_layout = QHBoxLayout()
        toggle_shortcut_layout.addWidget(self.toggle_shortcut_label)
        toggle_shortcut_layout.addWidget(modify_toggle_shortcut_btn)
        layout.addLayout(toggle_shortcut_layout)

        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
        layout.addWidget(line)

        # 在中间提示"按"ctrl+w"关闭窗口
        self.center_label = QLabel("<b>按'ctrl+w'关闭窗口</b><br><b>Press 'ctrl+w' to close the window")
        self.center_label.setAlignment(Qt.AlignCenter)
        layout.addWidget(self.center_label)

    def shortcutdef(self, key):
        return self.shortcutdict.get(key, '')

    def set_direction(self, direction, activater, button):
        self.is_active = activater
        if self.is_active:
            self.last_mouse_pos = QPoint(*pyautogui.position())
            self.reset_button_styles()
            button.setStyleSheet("background-color: green;")
            self.activate_btn.setStyleSheet("background-color: green;")
        self.direction = direction * -1
        self.emit_thread_signals()

    def set_custom_direction(self):
        try:
            direction = float(self.direction_input.toPlainText())
            self.set_direction(direction, True, self.custom_btn)
        except ValueError:
            self.set_direction(0, True, self.custom_btn)
            self.show_error_message()

    def show_error_message(self):
        msg_box = QMessageBox()
        msg_box.setIcon(QMessageBox.Warning)
        msg_box.setWindowTitle("输入无效(Invalid input)")
        msg_box.setText("输入无效，默认水平移动\nInvalid input, default horizontal move\n请输入有效的角度(按'Esc'或'ctrl+k'关闭)\nPlease enter a valid angle to continue\n(press 'Esc' or 'ctrl+k' to close the message box)")
        msg_box.setStandardButtons(QMessageBox.Ok)
        shortcutck = QShortcut(QKeySequence("Ctrl+K"), msg_box)
        shortcutck.activated.connect(msg_box.close)
        msg_box.exec_()

    def toggle_control(self):
        self.is_active = not self.is_active
        if self.is_active:
            self.last_mouse_pos = QPoint(*pyautogui.position())
            self.reset_button_styles()
            self.activate_btn.setStyleSheet("background-color: green;")
        else:
            self.reset_button_styles()
        self.emit_thread_signals()

    def reset_button_styles(self):
        for btn in [self.horizontal_btn, self.vertical_btn, self.custom_btn, self.activate_btn]:
            btn.setStyleSheet("")

    def emit_thread_signals(self):
        self.mouse_control_thread.update_is_active.emit(self.is_active)
        self.mouse_control_thread.update_direction.emit(self.direction)
        self.mouse_control_thread.update_last_mouse_pos.emit(self.last_mouse_pos)
        self.mouse_control_thread.update_amplitude_limit.emit(self.amplitude_limit)

    def closeEvent(self, event):
        self.mouse_control_thread.stop()
        self.mouse_control_thread.wait()
        keyboard.unhook_all()
        event.accept()

class MouseControlThread(QThread):
    update_is_active = pyqtSignal(bool)
    update_direction = pyqtSignal(float)
    update_last_mouse_pos = pyqtSignal(QPoint)
    update_amplitude_limit = pyqtSignal(float) 

    def __init__(self, app):
        super().__init__()
        self.app = app
        self.running = True
        self.is_active = False
        self.direction = 0
        self.last_mouse_pos = QPoint(*pyautogui.position())
        self.amplitude_limit = 0.1

        self.update_is_active.connect(self.set_is_active)
        self.update_direction.connect(self.set_disdirection)
        self.update_last_mouse_pos.connect(self.set_last_mouse_pos)
        self.update_amplitude_limit.connect(self.set_amplitude_limit)
        
    def set_is_active(self, is_active):
        self.is_active = is_active

    def set_disdirection(self, direction):
        self.direction = direction

    def set_last_mouse_pos(self, last_mouse_pos):
        self.last_mouse_pos = last_mouse_pos

    def set_amplitude_limit(self, amplitude_limit):
        self.amplitude_limit = amplitude_limit

    def run(self):
        while self.running:               
            last_x, last_y = self.last_mouse_pos.x(), self.last_mouse_pos.y()
            if self.is_active and self.last_mouse_pos is not None:
                current_pos = QPoint(*pyautogui.position())
                current_x, current_y = current_pos.x(), current_pos.y()
                clib.control_mouse_position_c(last_x, last_y, current_x, current_y, self.direction, self.amplitude_limit)

            time.sleep(0.01)

    def stop(self):
        self.running = False

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = MouseControlApp()
    ex.show()
    sys.exit(app.exec_())
